#include "Range.h"
#include <iostream>

Texture CRange::UnitTexture;
int CRange::RangeCounter = 0;

CRange::CRange(State state):ChargeTime(1)
{
	SetSize(20);
	this->state = STATE_IDLE;
	SetMaxHealth(100);
	SetHealth(GetMaxHealth());
	SetDamage(10);
	SetDetRadius(GetSize() + 300);
	SetAtkRadius(GetSize() + 150);
	SetAttackRate(5.0f);
	SetSpeed(20.f);
	Target = NULL;
	str = "IDLE";
	Charge = 0;	
	FullShield = 60;
	ShieldHealth = FullShield;
	LoadTGA(&UnitTexture, "bin/archer.tga");
	StunCoolDown = 0;
	role = ROLE_DPS;
	RangeCounter++;
	RangeCount = RangeCounter;
	Message = "";
	rolestr = "DPS";
}

CRange::~CRange()
{

}

void CRange::ChangeState()
{
	// if else statement
	if(role == ROLE_DPS)
	{
		if (state == STATE_IDLE)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				Vector3D CurrentTarget(Target->GetPos() - this->GetPos());
				if(((this->GetDetRadius()+Target->GetSize())*(this->GetDetRadius()+Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHASE;
				}
			}
		}
		else if (state == STATE_CHASE)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
				if (((this->GetDetRadius() + Target->GetSize())*(this->GetDetRadius() + Target->GetSize())) < (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_IDLE;
				}
				else if(((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHARGE;
				}
			}
			else
			{
				state = STATE_IDLE;
			}
			//state = STATE_CHARGE;
		}
		else if (state == STATE_CHARGE)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
				if(((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) < (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHASE;
				}
				else if(Charge >= ChargeTime)
				{
					state = STATE_ATTACK;
					Charge = 0;
				}
			}		
			else
				state = STATE_CHASE;
			//state = STATE_ATTACK;
		}
		else if (state == STATE_ATTACK)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				Vector3D CurrentTarget(Target->GetPos() - this->GetPos());
				if(this->GetHealth() <= 40 && rand() % 10 <= 4)
				{
					state = STATE_GUARD;
				}
				else if(((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHARGE;
				}
				else if(((this->GetDetRadius()+Target->GetSize())*(this->GetDetRadius()+Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHASE;
				}

			}
			else
				state = STATE_CHARGE;
			//state = STATE_GUARD;
		}
		else if (state == STATE_GUARD)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				Vector3D CurrentTarget(Target->GetPos() - this->GetPos());
				if(ShieldHealth <= 0)
					state = STATE_STUN;
				else if (((this->GetDetRadius() + Target->GetSize())*(this->GetDetRadius() + Target->GetSize())) < (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_IDLE;
				}
			}
			else
				state = STATE_IDLE;
		}
		else if(state == STATE_STUN)
		{
			if(StunCoolDown >= 5)
				state = STATE_IDLE;
		}
		else if(state ==  STATE_REPLACING)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() == this->GetTeam())
			{
				Vector3D CurrentTarget(Target->GetPos() - this->GetPos());
				if (((this->GetSize() + Target->GetSize())*(this->GetSize() + Target->GetSize())) > (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_IDLE;
				}
			}
			else
				state = STATE_IDLE;
		}
		else if(state ==  STATE_RECOVERING)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() == this->GetTeam())
			{
				Vector3D CurrentTarget(Target->GetPos() - this->GetPos());
				if (((this->GetSize() + Target->GetSize())*(this->GetSize() + Target->GetSize())) > (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_IDLE;
					CRange* Stunned = dynamic_cast<CRange*>(Target);
					Stunned->state = STATE_IDLE;
				}
			}
			else
				state = STATE_IDLE;
		}
	}
	else if(role == ROLE_HEALER)
	{
		if (state == STATE_IDLE)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() == this->GetTeam() && Target->GetHealth() != Target->GetMaxHealth())
			{
				state = STATE_CHASE;
			}
			else 
			{
				role = ROLE_DPS;
			}
		} 
		else if (state == STATE_CHASE)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() == this->GetTeam() && Target->GetHealth() != Target->GetMaxHealth())
			{
				Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
				if(((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHARGE;
				}
			}
			else
				state = STATE_IDLE;
		}
		else if (state == STATE_CHARGE)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() == this->GetTeam() && Target->GetHealth() != Target->GetMaxHealth())
			{
				Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
				if(((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) < (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHASE;
				}
				else if(Charge >= ChargeTime)
				{
					state = STATE_HEAL;
					Charge = 0;
				}
			}		
			else
				state = STATE_CHASE;
		}
		else if (state == STATE_HEAL)
		{
			if (Target != NULL && Target->GetActive() && Target->GetTeam() != this->GetTeam())
			{
				Vector3D CurrentTarget(Target->GetPos() - this->GetPos());
				if(((this->GetAtkRadius() + Target->GetSize())*(this->GetAtkRadius() + Target->GetSize())) >= (CurrentTarget.Dot(CurrentTarget)))
				{
					state = STATE_CHARGE;
				}
			}
			else 
				state = STATE_CHARGE;
		}
	}
}

void CRange::Respond(float dt)
{
	switch (state)
	{
	case CRange::STATE_IDLE:
		str = "IDLE";
		break;
	case CRange::STATE_CHASE:
		{
			str = "CHASE";
			Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
			this->SetPos(this->GetPos() + dt*CurrentTarget.Normalized()*this->GetSpeed());
		}
		break;
	case CRange::STATE_CHARGE:
		{
			str = "CHARGE";
			//Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
			Charge += dt;
		}
		break;
	case CRange::STATE_ATTACK:

		if(Target!=NULL)
		{
			CUnit* Arrow = new CArrow(this, Target);
			Arrow->SetTeam(this->GetTeam());
			FSM::GetInstance()->GetUnitList().push_back(Arrow);
		}

		str = "ATTACK";
		break;
	case CRange::STATE_GUARD:
		str = "GUARD";
		break;
	case CRange::STATE_STUN:
		StunCoolDown += dt;
		str = "STUN";
		break;
	case State::STATE_HEAL:
		str = "HEAL";
		if(Target!=NULL)
		{
			CUnit* Arrow = new CArrow(this, Target, CArrow::ARROW_HEAL);
			Arrow->SetTeam(this->GetTeam());
			FSM::GetInstance()->GetUnitList().push_back(Arrow);
		}
		break;
	case State::STATE_REPLACING:
		{
			str = "REPLACING";
			Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
			this->SetPos(this->GetPos() + dt*CurrentTarget.Normalized()*this->GetSpeed()* 10);
		}
		break;


	case State::STATE_RECOVERING:
		{
			str = "RECOVERING";
			Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
			this->SetPos(this->GetPos() + dt*CurrentTarget.Normalized()*this->GetSpeed()* 5);
		}
		break;
	default:
		break;
	}

	switch (role)
	{
	case ROLE_DPS:
		rolestr = "DPS";
		break;
	case ROLE_HEALER:
		rolestr = "HEALER";
		break;
	default:
		break;
	}
	//std::cout << "This Unit is in" << state << std::endl;
}

void CRange::Update(float Time)
{
	//static float count = 0; // Initialise Counter
	//count += Time;

	ChangeState(); // Change state
	//count = 0; // Reset Counter
	Respond(Time); // Respond
}

void CRange::Render(void)
{
	glEnable(GL_BLEND);
	if (this->GetToggle())
	{
		// Detection radius
		glPushMatrix();
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(1.0f, 1.0f, 0.0f, 0.2f);
		glTranslatef(GetPos().x, GetPos().y, GetPos().z);
		glScalef(GetDetRadius(), GetDetRadius(), 1);
		glutSolidSphere(1, 20, 20);
		glPopMatrix();

		glPushMatrix();
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(1.0f, 0.0f, 0.0f, 0.2f);
		glTranslatef(GetPos().x, GetPos().y, GetPos().z);
		glScalef(GetAtkRadius(), GetAtkRadius(), 1);
		glutSolidSphere(1, 20, 20);
		glPopMatrix();
	}

	glEnable(GL_TEXTURE_2D);
	glPushMatrix();
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glBindTexture(GL_TEXTURE_2D, UnitTexture.texID);
	glColor3f(1.0f, 1.0f, 1.0f);
	glTranslatef(GetPos().x, GetPos().y, GetPos().z);
	glScalef(GetSize(), GetSize(), 1);

	glBegin(GL_QUADS);
	glTexCoord2f(0, 0); glVertex2f(-2, -2);
	glTexCoord2f(1, 0); glVertex2f(2, -2);
	glTexCoord2f(1, 1); glVertex2f(2, 2);
	glTexCoord2f(0, 1); glVertex2f(-2, 2);
	glEnd();

	glPopMatrix();
	glDisable(GL_TEXTURE_2D);
	glDisable(GL_BLEND);

	RenderHealthBar();
	RenderShield();
	PrintState(GetPos().x - 50, GetPos().y - GetSize()- 40, 1, 1, 1, "ROLE: ");
	PrintState(GetPos().x + 5, GetPos().y - GetSize() - 40 , 1, 1, 1, rolestr);
	PrintState(GetPos().x - 55, GetPos().y + 40, 1, 1, 1, "STATE: ");
	PrintState(GetPos().x + 10, GetPos().y + 40, 1, 1, 1, str);
	string UnitCounter = "Range"  + to_string(static_cast<long long>(RangeCount));
	char* UnitCounterC = new char[UnitCounter.length() + 1];
	strcpy(UnitCounterC, UnitCounter.c_str());
	PrintState(GetPos().x - GetSize(), GetPos().y + GetSize() + 40, GetColor().x, GetColor().y, GetColor().z, UnitCounterC);
	PrintState(GetPos().x - GetSize(), GetPos().y + GetSize() + 60, 1, 1, 1, Message);
}

void CRange::UnitInteraction(CUnit* go_Unit)
{
	if(role == ROLE_DPS)
	{
		if(this->GetTeam() != go_Unit->GetTeam())
		{
			if (CUnit* test = dynamic_cast<CUnit*>(go_Unit))
			{
				if (go_Unit == Target || state == STATE_REPLACING || state == STATE_RECOVERING)
					return;
				else if (Target == NULL || !Target->GetActive() || this->GetTeam() == Target->GetTeam())
				{
					Target = go_Unit;
					return;
				}
				else
				{
					Vector3D CurrentTarget = Target->GetPos() - this->GetPos();
					Vector3D ThisTarget = go_Unit->GetPos() - this->GetPos();

					if (ThisTarget.Dot(ThisTarget) < CurrentTarget.Dot(CurrentTarget))
					{
						Target = go_Unit;
					}
				}
			}
		}
	}
	else if(role == ROLE_HEALER)
	{
		if(this->GetTeam() == go_Unit->GetTeam())
		{
			if (CMelee* Melee = dynamic_cast<CMelee*>(go_Unit))
			{
				if (Melee == Target)
					return;
				else if (Target == NULL || !Target->GetActive())
				{
					Target = Melee;
					return;
				}
				else if(state == STATE_CHASE) 
				{
					if (Target->GetHealth() > Melee->GetHealth())
					{
						Target = Melee;
					}
				}
			}
		}
	}
}

void CRange::PrintState(int x, int y, float r, float g, float b, char *str)
{
	va_list args;	//  Variable argument list
	int len;		//	String length
	int i;			//  Iterator
	char * text;	//	Text

	//  Initialize a variable argument list
	va_start(args, str);

	//  Return the number of characters in the string referenced the list of arguments.
	//  _vscprintf doesn't count terminating '\0' (that's why +1)
	len = _vscprintf(str, args) + 1;

	//  Allocate memory for a string of the specified size
	text = (char *)malloc(len * sizeof(char));

	//  Write formatted output using a pointer to the list of arguments
	vsprintf_s(text, len, str, args);

	//  End using variable argument list 
	va_end(args);

	glColor3f(r, g, b);
	//  Specify the raster position for pixel operations.
	glRasterPos3f(x, y, 0);


	//  Draw the characters one by one
	for (i = 0; text[i] != '\0'; i++)
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_18, text[i]);

	//  Free the allocated memory for the string
	free(text);
}

void CRange::DealDamageTo(CUnit* go_Unit)
{
	if(CArrow* arrow = dynamic_cast<CArrow*>(go_Unit))
	{
		if(arrow->state == CArrow ::ARROW_DAMAGE)
		{
			if(state == STATE_GUARD)
			{
				ShieldHealth -= go_Unit->GetDamage();
				if(ShieldHealth < 0)
					ShieldHealth = 0;
			}
			else 
				this->SetHealth(this->GetHealth() - go_Unit->GetDamage());
		}
		else if(arrow->state == CArrow ::ARROW_HEAL)
		{
			this->SetHealth(this->GetHealth() + go_Unit->GetDamage());

			if(GetHealth() > GetMaxHealth())
				this->SetHealth(GetMaxHealth());
		}
	}
	else
	{

		if(state == STATE_GUARD)
		{
			ShieldHealth -= go_Unit->GetDamage();
			if(ShieldHealth < 0)
				ShieldHealth = 0;
		}
		else 
			this->SetHealth(this->GetHealth() - go_Unit->GetDamage());
	}
}

void CRange::RenderShield(void)
{
	glDisable(GL_BLEND);
	glPushMatrix();
	glColor3f(1.0f, 1.0f, 1.0f);
	glTranslatef(this->GetPos().x - 30, this->GetPos().y - 50, 0);
	glBegin(GL_LINE_LOOP);
	glVertex2f(0-1, 0-1);
	glVertex2f(FullShield+1, 0-1);
	glVertex2f(FullShield+1, 10+1);
	glVertex2f(0-1, 10+1);
	glEnd();
	glColor3f(0.2f, 0.2f, 0.6f);
	glBegin(GL_QUADS);
	glVertex2f(0, 0);
	glVertex2f(FullShield, 0);
	glVertex2f(FullShield, 10);
	glVertex2f(0, 10);
	glEnd();
	glPushMatrix();
	glColor3f(0.5f, 0.8f, 1.0f);
	glBegin(GL_QUADS);
	glVertex2f(0, 0);
	glVertex2f(ShieldHealth, 0);
	glVertex2f(ShieldHealth, 10);
	glVertex2f(0, 10);
	glEnd();
	glPopMatrix();
	glPopMatrix();
	glEnable(GL_BLEND);		
}

void CRange::SetShield(int ShieldHealth)
{
	this->ShieldHealth = ShieldHealth;
}

int CRange::GetShield(void)
{
	return ShieldHealth;
}

int CRange::GetFullShield(void)
{
	return FullShield;
}

int CRange::GetRangeCount(void)
{
	return RangeCount;
}

void CRange::CheckMessage(std::vector<MessageBoard*> &Msgb)
{
	for (int i = 0; i < Msgb.size(); i++)
	{
		if (Msgb[i]->Getto() == this)
			return;
	}

	for (int i = 0; i < Msgb.size(); i++)
	{
		if (Msgb[i]->Getfrom()->GetTeam() == this->GetTeam())
		{
			if (Msgb[i]->Getsearchfor() == "RANGE")
			{
				if (Msgb[i]->Getfrom() != this && GetCurrentMsg()->Getmsg() == "NULL")
				{
					if (Msgb[i]->Getmsg() == "NEED HP" )
					{
						if(Msgb[i]->Getto() == NULL)
						{
							state = State::STATE_IDLE;
							role = ROLE_HEALER;
							Target = Msgb[i]->Getfrom();
							Msgb[i]->SetRecv(this);
						}
						else if(CRange* Range = dynamic_cast<CRange*>(Msgb[i]->Getto()))
						{
							if(Range->role == ROLE_DPS && this->role == ROLE_HEALER)
							{
								state = State::STATE_IDLE;
								role = ROLE_HEALER;
								Target = Msgb[i]->Getfrom();
								Msgb[i]->SetRecv(this);
							}
						}
					}
					else if (Msgb[i]->Getmsg() == "REPLACE ME")
					{
						if(Msgb[i]->Getto() == NULL && role == ROLE_DPS)
						{
							state = STATE_REPLACING;
							Target = Msgb[i]->Getfrom();
							Msgb[i]->SetRecv(this);
						}
					}
					else if (Msgb[i]->Getmsg() == "RECOVER ME")
					{
						if(Msgb[i]->Getto() == NULL)
						{
							state = State::STATE_RECOVERING;
							Target = Msgb[i]->Getfrom();
							Msgb[i]->SetRecv(this);
						}
					}
				}
			}
		}
	}
}

void CRange::ShieldTo(CUnit* go_Unit)
{
	if (CMelee* melee = dynamic_cast<CMelee*>(go_Unit))
	{
		if (melee->state = CMelee::STATE_GIVESHIELD)
		{
			this->SetShield(FullShield);
		}
	}
}